package org.x.common.web.exception;

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.x.common.base.result.Result;
import org.x.common.base.result.ResultCode;
import java.util.stream.Collectors;
import static org.x.common.base.result.ResultCode.*;

/**
 * dubbo调用服务异常的时候，这里没有拦截到
 */

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 业务异常（已知预期异常）
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        log.warn("[业务异常] code={}, msg={}", e.getCode(), e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    // 新增Dubbo调用异常处理
    @ExceptionHandler(RpcException.class)
    public Result<?> handleRpcException(RpcException e) {
        log.error("[Dubbo服务调用异常] 状态码={}, 错误信息={}", e.getCode(), e.getMessage(), e);
        // 根据错误类型细化错误提示
        ResultCode message = switch (e.getCode()) {
            case RpcException.TIMEOUT_EXCEPTION -> SERVICE_TIMEOUT;
            case RpcException.NETWORK_EXCEPTION -> NETWORK_ERROR;
            case RpcException.SERIALIZATION_EXCEPTION -> SERIALIZATION_ERROR;
            default -> SERVICE_UNAVAILABLE;
        };
        return Result.error(message);
    }

    // 参数校验异常（JSR-303）
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleValidException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(fieldError -> fieldError.getField() + fieldError.getDefaultMessage())
                .collect(Collectors.joining("；"));
        log.warn("[参数校验异常] {}", message);
        return Result.error(PARAM_VALID_ERROR);
    }
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<?> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
        log.error("参数类型错误: {}", ex.getMessage());
        return Result.error(ResultCode.PARAM_TYPE_ERROR);
    }

    // 分布式调用异常（Dubbo）
    @ExceptionHandler(ApiException.class)
    public Result<?> handleRpcException(ApiException e) {
        log.error("[远程调用异常] errorCode={},", e.getCode(), e);
        return Result.error(503, "服务调用失败，请稍后重试");
    }

    // 处理 404 异常
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<?> handle404(NoHandlerFoundException ex) {
        return Result.error(404, "接口不存在");
    }


    // 系统级异常（兜底处理）
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        log.error("[系统异常] {}", e.getMessage(), e);
        return Result.error(500, "系统繁忙，请稍后再试");
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
        log.error("请求方法不支持: ", exception);
        return Result.error(ResultCode.METHOD_NOT_ALLOWED);
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<?> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException exception) {
        log.error("媒体类型不支持: ", exception);
        return Result.error(UNSUPPORTED_MEDIA_TYPE);
    }

    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public Result<?> handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException exception) {
        log.error("媒体类型无法接受: ", exception);
        return Result.error(MEDIA_TYPE_NOT_ACCEPTABLE);
    }

    @ExceptionHandler(MissingPathVariableException.class)
    public Result<?> handleMissingPathVariableException(MissingPathVariableException exception) {
        log.error("路径变量缺失: ", exception);
        return Result.error(MISSING_PATH_VARIABLE);
    }

}